home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright (c) 2002, Jonathan Feinberg
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote
- products derived from this software without specific prior written
- permission.
-
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- $Id: KeepSoundAwake.mm,v 1.3 2002/04/07 17:28:32 jdf Exp $
-
- */
-
- #include <Foundation/Foundation.h>
-
- // For CFRunLoop stuff
- #include <CoreFoundation/CoreFoundation.h>
-
- // For ApplicationEventLoop stuff
- #include <Carbon/Carbon.h>
-
- // For NSSound
- #include <AppKit/AppKit.h>
-
- // For detecting whether the charger is plugged in
- #include <IOKit/pwr_mgt/IOPM.h>
- #include <IOKit/pwr_mgt/IOPMLib.h>
-
- // For retrieving preferences
- #include "KSAPrefConstants.h"
-
- // For debug and status messages
- #include <syslog.h>
-
-
- // Globals used in determining whether we're running on battery
- mach_port_t masterPort;
- NSString *flagsString;
-
- // We keep things that might be affected by pref changes in these globals
- CFRunLoopTimerRef soundTimer;
- BOOL shouldWatchCharger;
-
- // The sound to play periodically
- NSSound *sound;
-
- // Are we running on battery power?
- // Thanks to Paul Haddad (paul@pth.com) for this code, and for the notion.
- int chargerIsConnected()
- {
- NSMutableArray *array;
- int newFlags;
-
- if (kIOReturnSuccess == IOPMCopyBatteryInfo(masterPort, (CFArrayRef *)&array))
- {
- newFlags = [[[array lastObject] objectForKey:flagsString] intValue];
- [array release];
- }
- else
- newFlags = kIOBatteryChargerConnect;
- return (newFlags & kIOBatteryChargerConnect);
- }
-
- // Called periodically by soundTimer
- void soundTimerCallback(CFRunLoopTimerRef timer, void *info)
- {
- if ((!shouldWatchCharger) || chargerIsConnected())
- [sound play];
- }
-
- // Called indirectly by DoQuitApp, below.
- void delayedQuit(CFRunLoopTimerRef timer, void *info)
- {
- QuitApplicationEventLoop();
- }
-
- /*
- This handler is installed to handle the "QUIT" AppleEvent
- It sets up a delayed call to "delayedQuit", above. If we
- do not delay the QuitApplicationEventLoop() call, then the
- AppleScript infrastructure gets confused, and may wind up
- starting and restarting this app, just to tell it to quit!
- */
- static pascal OSErr DoQuitApp(const AppleEvent *message,
- AppleEvent *reply,
- long refCon)
- {
-
- CFRunLoopTimerRef qtimer = CFRunLoopTimerCreate(
- kCFAllocatorDefault,
- CFAbsoluteTimeGetCurrent() + 1,
- 0,
- 0,
- 0,
- delayedQuit,
- NULL
- );
- CFRunLoopAddTimer( CFRunLoopGetCurrent(), qtimer, kCFRunLoopCommonModes );
-
- return noErr;
- }
-
- void setupSoundTimer(int period)
- {
- if (soundTimer)
- {
- CFRunLoopRemoveTimer( CFRunLoopGetCurrent(), soundTimer, kCFRunLoopCommonModes );
- CFRelease(soundTimer);
- soundTimer = NULL;
- }
- soundTimer = CFRunLoopTimerCreate(
- kCFAllocatorDefault,
- CFAbsoluteTimeGetCurrent(),
- period,
- 0,
- 0,
- soundTimerCallback,
- NULL
- );
- CFRunLoopAddTimer( CFRunLoopGetCurrent(), soundTimer, kCFRunLoopCommonModes );
- }
-
- void refreshPrefs()
- {
- CFPreferencesAppSynchronize((CFStringRef)suiteID);
-
- NSString *s_period =
- (NSString *)CFPreferencesCopyAppValue((CFStringRef)MFKSASeconds,
- (CFStringRef)suiteID);
- int period = s_period ? [s_period intValue] : 20;
- [s_period release];
-
- NSString *s_chargerAwareness =
- (NSString *)CFPreferencesCopyAppValue((CFStringRef)MFKSAWatchCharger,
- (CFStringRef)suiteID);
- shouldWatchCharger = s_chargerAwareness ? [s_chargerAwareness intValue] : YES;
- [s_chargerAwareness release];
-
- setupSoundTimer(period);
- }
-
- // This routine is called when the preferences pane wants to tell
- // us that something has changed. It simply calls refreshPrefs().
- void prefsChanged(CFNotificationCenterRef center, void *observer,
- CFStringRef notificationName, const void *observedObject,
- CFDictionaryRef userInfo)
- {
- refreshPrefs();
- }
-
- int main (int argc, const char * argv[])
- {
- // Set up battery detection globals
- IOMasterPort( MACH_PORT_NULL, &masterPort );
- flagsString = [[NSString alloc] initWithCString:kIOBatteryFlagsKey];
-
- // Compile with NOISY defined to make KeepSoundAwake noisy instead of silent
- // for debugging porpoises
- #ifdef NOISY
- sound =
- [[NSSound alloc] initWithContentsOfFile: @"/System/Library/Sounds/Temple.aiff"
- byReference: NO];
- #else
- // Determined from a hex dump of a "blank" AIFF sound file.
- char AIFFbytes[54] = {
- 'F', 'O', 'R', 'M', 0x00, 0x00, 0x00, 0x2E,
- 'A', 'I', 'F', 'F', 'C', 'O', 'M', 'M',
- 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x10, 0x40, 0x0E, 0xAC, 0x44,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'S', 'S', 'N', 'D',
- 0x00, 0x00, 0x00, 0x08,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- NSData *data = [[NSData alloc] initWithBytes:AIFFbytes length:54];
- sound = [[NSSound alloc] initWithData:data];
- [data release];
- #endif
-
- // Get our preferences and start the sound timer
- refreshPrefs();
-
- // Listen for notification from the preference pane
- void *observer; // an arbitrary pointer, ignored
- CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
- observer,
- prefsChanged,
- (CFStringRef)MFKSAPrefsChangedNotification,
- (CFStringRef)suiteID,
- CFNotificationSuspensionBehaviorDeliverImmediately);
-
- // Intercept "quit" Apple Event
- AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
- NewAEEventHandlerUPP(DoQuitApp), 0, FALSE);
-
- syslog(1, "starting up");
- RunApplicationEventLoop();
-
- syslog(1, "cleaning up and exiting");
- /* No fancy cleanup needed. */
-
- return 0;
- }
-